//
// Created by vivi on 24/09/2017.
//

#ifndef HAIRCORE_HAIRINTERPOLATIONSIMULATOR_H
#define HAIRCORE_HAIRINTERPOLATIONSIMULATOR_H

#include <string>
#include <istream>
#include <fstream>
#include "HairSelleSimulator.h"

namespace XRwy {

	struct HairInterpolationSimulatorConfiguration: public HairSelleSimulatorConfiguration {
		std::string weights_file_path;

		HairInterpolationSimulatorConfiguration(const HairSimulatorConfDict & d):
				HairSelleSimulatorConfiguration(d) {

			HairSimulatorConfDictHelperUtility u;
			weights_file_path = u.get_string(d, "weights_file_path");
		}
	};

	struct HairInterpolationSimulatorWeightsInfoIDAndWeight {
		size_t id;
		float weight;
	};

	class HairInterpolationSimulator;

	class HairInterpolationSimulatorWeightsInfo {

		using IDAndWeight = HairInterpolationSimulatorWeightsInfoIDAndWeight;
		using IDAndWeightList = std::list<IDAndWeight>;

		friend class HairInterpolationSimulator;

	public:
		HairInterpolationSimulatorWeightsInfo(std::istream &input_stream) {
			init(input_stream);
		}

		HairInterpolationSimulatorWeightsInfo(const char *file_path) {
			std::ifstream fin(file_path, std::ios::in | std::ios::binary);
			init(fin);
		}

		HairInterpolationSimulatorWeightsInfo(const std::string file_path):
				HairInterpolationSimulatorWeightsInfo(file_path.c_str()) {}

		std::vector<size_t> guide_ids; //the vector of guide hair id
		std::vector<IDAndWeightList> weights; //the weights

		size_t nstrand;
		size_t nguide;

	private:
		void init(std::istream &input_stream);
	};

	class HairInterpolationSimulator: public HairBaseSimulator {
	public:
		HairInterpolationSimulator(const HairReader & reader, const HairInterpolationSimulatorConfiguration &conf);

		virtual ~HairInterpolationSimulator() {
			if (copy_buffer != nullptr)
				delete [] copy_buffer;
			copy_buffer = nullptr;
		}

		virtual void strand_dynamic(const Eigen::Affine3f &, const float t);

	private:
		HairInterpolationSimulatorWeightsInfo weights_info;
		HairSelleSimulator base_simulator;

		size_t particle_count;
		size_t strand_count;

		std::vector<Eigen::Vector3f> pos_buffer;
		std::vector<Eigen::Vector3f> vel_buffer;
		std::vector<Eigen::Vector3f> strand_rest_root_pos;

		float *copy_buffer = nullptr;

		//fill the pos_buffer and vel_buffer based on base simulator, usually called after the base simulator simulation
		//completes
		void interpolation(const Eigen::Affine3f &, const float t);
	};
}

#endif //HAIRCORE_HAIRINTERPOLATIONSIMULATOR_H
